﻿// -----------------------------------------------------------------------
// <copyright file="DoWhileExpression.cs" company="none">
// Copyright (c) 2012 M. Alberti, xanatos(at)live.it
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
// </copyright>
// -----------------------------------------------------------------------

namespace FluentStatement
{
    using System;
    using System.Collections.Generic;
    using System.Linq.Expressions;
    using Utilities;

    /// <summary>
    /// 
    /// </summary>
    public class DoWhileExpression : Expression
    {
        /// <summary>
        /// 
        /// </summary>
        protected readonly Expression Body2;

        /// <summary>
        /// 
        /// </summary>
        protected readonly Expression Condition2;

        /// <summary>
        /// 
        /// </summary>
        protected readonly LabelTarget BreakLabel2;

        /// <summary>
        /// 
        /// </summary>
        protected readonly LabelTarget ContinueLabel2;

        /// <summary>
        /// 
        /// </summary>
        protected readonly Expression Expression2;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="body"></param>
        /// <param name="condition"></param>
        /// <param name="break"></param>
        /// <param name="continue"></param>
        public DoWhileExpression(Expression body, Expression condition, LabelTarget @break, LabelTarget @continue)
        {
            if (body == null)
            {
                throw new ArgumentNullException("body");
            }

            if (condition == null)
            {
                throw new ArgumentNullException("condition");
            }

            if (condition.Type != typeof(bool) && (condition.Type != typeof(void) || !(condition is DefaultExpression)))
            {
                throw new ArgumentException("condition");
            }

            if (@break != null && !body.Type.IsSimplyAssignableFrom(@break.Type))
            {
                throw new ArgumentException("@break");
            }

            this.Body2 = body;
            this.Condition2 = condition;
            this.BreakLabel2 = @break ?? Expression.Label(body.Type);
            this.ContinueLabel2 = @continue ?? Expression.Label();

            this.Expression2 = this.InternalReduce();
        }

        /// <summary>
        /// 
        /// </summary>
        public Expression Body
        {
            get
            {
                return this.Body2;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public new Expression Condition
        {
            get
            {
                return this.Condition2;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public LabelTarget BreakLabel
        {
            get
            {
                return this.BreakLabel2;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public LabelTarget ContinueLabel
        {
            get
            {
                return this.ContinueLabel2;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public override ExpressionType NodeType
        {
            get
            {
                return ExpressionType.Extension;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public override bool CanReduce
        {
            get
            {
                return true;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public override Type Type
        {
            get
            {
                return this.BreakLabel.Type;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override Expression Reduce()
        {
            return this.Expression2;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        protected Expression InternalReduce()
        {
            var block = new List<Expression>();

            ParameterExpression breakValue = null;

            if (this.Condition.Type == typeof(bool))
            {
                if (this.Type != typeof(void))
                {
                    breakValue = Expression.Variable(this.Type);
                }
            }

            Expression expression;

            if (breakValue != null)
            {
                block.Add(Expression.Assign(breakValue, this.Body));
            }
            else
            {
                block.Add(this.Body);
            }

            LabelTarget continueLabel = null;

            if (this.Condition.Type == typeof(bool))
            {
                block.Add(Expression.Label(this.ContinueLabel));
                block.Add(Expression.IfThen(Expression.Not(this.Condition), Expression.Break(this.BreakLabel, breakValue)));
            }
            else
            {
                continueLabel = this.ContinueLabel;
            }

            if (breakValue != null)
            {
                expression = Expression.Loop(Expression.Block(new[] { breakValue }, block), this.BreakLabel, continueLabel);
            }
            else
            {
                expression = Expression.Loop(Expression.Block(block), this.BreakLabel, continueLabel);
            }

            return expression;
        }
    }
}